package com.mswyt.utils.msThread;

import com.mswyt.utils.base.MsLogUtils;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.*;

/**
 * @ClassName: MsThreadUtils
 * @Description: 线程池工具类
 */
@Component
public class MsThreadUtils {

    //cpu数量
    private static final int CPU_NUM = Runtime.getRuntime().availableProcessors();
    //最大核心线程数量
    private static final int MAX_POOL_SIZE = CPU_NUM * 2;
    //队列数量，默认1w
    private static final int MAX_QUEUE_SIZE = 10000;
    //线程存活时间
    private static final int KEEP_ALIVE_TIME = 60;

    //线程池缓存
    private static Map<String, ThreadPoolExecutor> executorMap = new ConcurrentHashMap<>();

    /**
     * 创建默认配置的线程池
     **/
    private synchronized static ThreadPoolExecutor createThreadPool() {
        BlockingQueue<Runnable> workQueue = new LinkedBlockingDeque<>();
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
        return new ThreadPoolExecutor(
                CPU_NUM,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                workQueue,
                threadFactory,
                rejectedExecutionHandler);
    }

    /**
     * 获取线程池执行器
     **/

    public static synchronized ThreadPoolExecutor getExecutor(String executorName) {
        ThreadPoolExecutor executor = executorMap.get(executorName);
        if (executor != null) return executor;
        //创建线程池
        MsLogUtils.info("开始创建线程池：{}", executorName);
        ThreadPoolExecutor poolExecutor = createThreadPool();
        executorMap.put(executorName, poolExecutor);
        return poolExecutor;
    }

    /**
     * 关闭线程池。
     * 如果在方法最开始地方调用，表示该方法每次被调用都会创建一个新的线程池
     *
     * @param executorName
     * @return
     * @author mashuai
     */

    public static synchronized void shutdown(String executorName) {
        getExecutor(executorName).shutdown();
        MsLogUtils.info("开始销毁线程池：{}", executorName);
        executorMap.remove(executorName);
    }

    /**
     * 不保证多个线程执行结果的顺序
     * 使用提供的执行器创建一个 CompletionService 用于执行基本任务,，可以直接返回先执行完成的线程的返回值，
     *
     * @param executorName 线程名称。如果已创建后，从缓存中获取线程池
     * @return CompletionService 调用其sumbit或者runable执行线程，take阻塞线程取值
     * @author mashuai
     */

    public static <V> CompletionService<V> getCompletionService(String executorName) {
        CompletionService<V> completionService = new ExecutorCompletionService<>(getExecutor(executorName));
        return completionService;
    }

    /**
     * 需要保证多线程返回结果值，可使用这个。效率比getCompletionService()获取的执行器慢一点。
     * 提供ExecutorService执行器。executorService.submit()
     *
     * @param executorName
     * @return executor
     * @author mashuai
     */

    public static <V> ExecutorService getExecutorService(String executorName) {
        ExecutorService executor = getExecutor(executorName);
        return executor;
    }

    /**
     * 如果涉及到，判断某个方法执行是否超时，可以使用这个方法，返回的是null说明该方法在规定时间内已经执行超时
     * 会响应中断，会阻塞，阻塞时间参照方法里参数timeout.timeUnit，当阻塞时间到了还没取得元素会返回null
     *
     * @param completionService
     * @param time              检查方法执行超时代时间，单位ms
     * @return 如果在规定时间内，还没返回结果，则返回null值
     * @author mashuai
     */

    public static <V> V getResult(CompletionService<V> completionService, long time) throws Exception {
        Future<V> future = completionService.poll(time, TimeUnit.MILLISECONDS);
        if (null == future) return null;
        return future.get();
    }

    /**
     * 阻塞方式获取现场返回的结果
     **/
    public static <V> V getResult(CompletionService<V> completionService) throws Exception {
        return completionService.take().get();
    }

    public static <V> V getResult(Future<V> future) throws Exception {
        return future.get();
    }

    /**
     * 判断future线程对象是否执行超时
     **/
    public static <V> V getResult(Future<V> future, long time) throws Exception {

        try {
            return future.get(time, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 线程休眠
     *
     * @param miss
     * @return
     * @author mashuai
     */

    public static void threadSleep(int miss) {
        try {
            Thread.sleep(miss);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
